import java.io.BufferedReader;
import java.util.*;

import ts.*;

public class AnalizadorSintactico {

	private AnalizadorLexico al;
	private Token nextToken;
	private Token oldToken;
	private String next;
	private HashMap<String, HashSet<String>> first;
	private static final boolean debug = false;
	private static String[] error;
	private TablaDeClases tc;
	//private boolean desapilar = false; // me dice si tengo que desapilar la  expresión

	private int indexIf = 0;
	private int indexWhile = 0;
	private int indexFor = 0;
	private int indexString = 0;
	private int indexCorto = 0;

	private Clase clase; // clase actual, ya sea en declaración o definición
	private Metodo metodo; // metodo o constructor actual, el que se está definiendo

	private String out = "; Zapata & Komañski Team\n\n";

	private void gen(String et, String s) {
		// System.out.print(et + ": ");
		// System.out.println(s);
		out = out + et + ": " + s + "\n";
	}

	private void gen(String s) {
		// System.out.println(s);
		out = out + s + "\n";
	}

	private void pp(String s) {
		System.out.println(s);
	}

	public AnalizadorSintactico(BufferedReader b) {
		al = new AnalizadorLexico(b);
		init();
	}

	private void advance() {
		// avanza al siguiente token
		oldToken = nextToken;
		nextToken = al.getNextToken();
		if (nextToken != null) {
			next = nextToken.id;
			debug(nextToken.lexema);
		}
		debug("next " + next);
	}

	private void init() {
		initClassTable();
		generateInit();
		initFirst();
		initErrors();
		advance();
	}

	private void initFirst() {
		// por ahora solo pongo los que necesito
		first = new HashMap<String, HashSet<String>>();

		HashSet<String> methodDef = new HashSet<String>();
		methodDef.add("identifier");
		first.put("MethodDef", methodDef);

		HashSet<String> classDef = new HashSet<String>();
		classDef.add("classDef");
		first.put("ClassDef", classDef);

		HashSet<String> class_ = new HashSet<String>();
		class_.add("class");
		first.put("Class", class_);

		HashSet<String> formalArgs = new HashSet<String>();
		formalArgs.add("(");
		first.put("FormalArgs", formalArgs);

		HashSet<String> primitiveType = new HashSet<String>();
		primitiveType.add("boolean");
		primitiveType.add("char");
		primitiveType.add("int");
		primitiveType.add("String");
		first.put("PrimitiveType", primitiveType);

		HashSet<String> type = new HashSet<String>(primitiveType);
		type.add("identifier");
		first.put("Type", type);
		
	

		HashSet<String> methodType = new HashSet<String>(type);
		methodType.add("void");
		first.put("MethodType", methodType);
		// for(String s:methodType) debug("MethodType: "+s);

		HashSet<String> method = new HashSet<String>(methodType);
		first.put("Method", method);

		HashSet<String> isCtor = new HashSet<String>(formalArgs);
		first.put("IsCtor", isCtor);

		HashSet<String> isMethod = new HashSet<String>(formalArgs);
		first.put("IsMethod", isMethod);

		HashSet<String> isField = new HashSet<String>();
		isField.add(",");
		isField.add(";");
		first.put("IsField", isField);

		HashSet<String> block = new HashSet<String>();
		block.add("{");
		first.put("Block", block);

		HashSet<String> literal = new HashSet<String>();
		literal.add("null");
		literal.add("true");
		literal.add("false");
		literal.add("intLiteral");
		literal.add("charLiteral");
		literal.add("stringLiteral");
		first.put("Literal", literal);

		HashSet<String> unaryOp = new HashSet<String>();
		unaryOp.add("!");
		unaryOp.add("+");
		unaryOp.add("-");
		first.put("UnaryOp", unaryOp);

		HashSet<String> expression = new HashSet<String>(literal);
		for (String s : unaryOp)
			expression.add(s);
		expression.add("identifier");
		expression.add("this");
		expression.add("(");
		expression.add("new");
		expression.add("super");
		first.put("Expression", expression);
		// for(String s:expression) debug("Expression: "+s);

		HashSet<String> statement = new HashSet<String>(block);
		statement.add(";");
		statement.add("if");
		statement.add("return");
		statement.add("for");
		statement.add("while");
		for (String s : first.get("Expression"))
			statement.add(s);
		first.put("Statement", statement);
		// for(String s:statement) debug("Statement: "+s);

		HashSet<String> primary = new HashSet<String>(literal);
		primary.add("identifier");
		primary.add("this");
		primary.add("(");
		primary.add("new");
		primary.add("super");
		first.put("Primary", primary);

		HashSet<String> primary2 = new HashSet<String>();
		primary2.add(".");
		first.put("Primary2", primary2);

		HashSet<String> actualArgs = new HashSet<String>();
		actualArgs.add("(");
		first.put("ActualArgs", actualArgs);

		HashSet<String> exprList = new HashSet<String>(expression);
		first.put("ExprList", exprList);
	}

	private void initErrors() {
		error = new String[500];
		error[0] = "El archivo se encuentra vacío";
		error[1] = "Se encontró código extra";
		error[2] = "Se espera \"classdef\"";
		error[3] = "Se espera un identificador a continuación de \"classdef\"";
		error[4] = "Se espera un identificador para el método";
		error[5] = "Se espera \"class\"";
		error[6] = "Se espera un identificador a continuación de \"class\"";
		error[7] = "Se espera un identificador para el método o campo";
		error[8] = "Se espera un identificador para el método";
		error[9] = "Se espera \";\"";
		error[10] = "Se espera un identificador a continuación de \"extends\"";
		error[11] = "Se espera un identificador para el método";
		error[12] = "Se espera un identificador para el método o campo";
		error[13] = "Se espera un identificador para el método";
		error[14] = "Se espera \"(\" como comienzo de los argumentos formales";
		error[15] = "Se espera \")\" como fin de los argumentos formales";
		error[16] = "Se espera un identificador para el argumento formal";
		error[17] = "Se espera un tipo primitivo: boolean, char, int, String";
		error[18] = "Se espera un identificador en la lista de declaración de variables";
		error[19] = "Se espera \"(\" a continuación de \"if\"";
		error[20] = "Se espera \")\" como fin de la condición de la sentencia if";
		error[21] = "Se espera \";\" como fin de la instrucción de retorno";
		error[22] = "Se espera \";\" como fin de la expresión";
		error[23] = "Se espera \"(\" a continuación de \"for\"";
		error[24] = "Se espera \";\" como separador";
		error[25] = "Se espera \";\" como separador";
		error[26] = "Se espera \")\" como fin del encabezado del for";
		error[27] = "Se espera una sentencia";
		error[28] = "Se espera \"(\" a continuación de \"while\"";
		error[29] = "Se espera \")\" como fin de la condición del while";
		error[30] = "Se espera un operador unario: !,+,-";
		error[31] = "Se espera \")\" como cierre de la expresión";
		error[32] = "Se espera un identificador a continuación de \"new\"";
		error[33] = "Se espera una expresión";
		error[34] = "Se espera \".\" a continuación de \"super\"";
		error[35] = "Se espera un identificador a continuación de \".\"";
		error[36] = "Se espera un identificador a continuación de \".\"";
		error[37] = "Se espera un identificador a continuación de \".\"";
		error[38] = "Se espera un literal: null, true, false, intLiteral, charLiteral, stringLiteral";
		error[39] = "Se espera \"(\" como inicio de los argumentos actuales";
		error[40] = "Se espera \")\" como fin de los argumentos actuales";
		error[41] = "Se espera \"{\" como inicio de la definición de la clase";
		error[42] = "Se espera \"}\" como fin de la definición de la clase";
		error[43] = "Se espera \";\" como fin de la definición del método";
		error[44] = "Se espera \"{\" como inicio de la clase";
		error[45] = "Se espera \"}\" como fi48n de la clase";
		error[46] = "Se espera \"{\" como inicio del bloque";
		error[47] = "Se espera \"}\" como fin del bloque";
		error[48] = "Se espera \";\" como fin de la definición del constructor";
		error[49] = "Se espera comienzo de argumentos formales o identificador de método";
		error[50] = "Se espera un identificador o argumentos formales";
		error[51] = "Se espera un tipo para el método";
		error[52] = "Se espera un tipo";
		error[53] = "Se espera continuación de definición de método o variable de instancia";
		error[54] = "Se espera continuación de definición de método o constructor";
		error[55] = "Se espera un operador unario o un primary";
		error[56] = "Se espera un \".\" luego de un \"this\"";

		error[100] = "NA";
		error[200] = "ERROR SEMANTICO";
		error[201] = "Nombre de clase repetido";
		error[202] = "La clase padre no ha sido declarada";
		error[203] = "La clase definida no ha sido declarada";
		error[204] = "La clase padre debe ser la misma en la definición que en la declaración";
		error[205] = "Existen clases declaradas que no han sido definidas";
		error[206] = "Una clase no puede heredar de sí misma";
		error[207] = "No puede haber dos parámetros formales con el mismo nombre";
		error[208] = "El nombre del constructor debe ser el mismo que el de la clase";
		error[209] = "Una clase debe tener exactamente un constructor";
		error[210] = "Los parámetros formales de la definición del constructor no coinciden con su declaración";
		error[211] = "El constructor declarado no ha sido definido";
		error[212] = "Una clase no puede tener más de un atributo con el mismo nombre";
		error[213] = "Un atributo no puede tener el mismo nombre que la clase a la que pertenece";
		error[214] = "El constructor definido no ha sido declarado";
		error[215] = "Un método no puede tener el mismo nombre que la clase a la que pertenece ni que una clase ancestro";
		error[216] = "Una clase no puede tener más de un método con el mismo nombre";
		error[217] = "El método definido no ha sido declarado";
		error[218] = "El tipo de retorno de la definición del método no coincide con su declaración";
		error[219] = "Los parámetros formales de la definición del método no coinciden con su declaración";
		error[220] = "Existen métodos declarados pero no definidos";
		error[221] = "Un atributo no puede tener el mismo nombre que un método que pertenece a la misma clase";
		error[222] = "Existen clases que fueron utilizadas pero no declaradas";
		error[223] = "Un método que sobreescribe a otro debe tener el mismo tipo de retorno";
		error[224] = "Un método que sobreescribe a otro debe tener la misma cantidad y tipo de parámetros";
		error[225] = "Debe declararse una clase Main";
		error[226] = "La clase Main debe tener declarado un constructor sin parámetros";
		error[227] = "El tipo de los atributos de instancia debe ser un tipo primitivo o una clase declarada";
		error[228] = "Solo se puede invocar métodos a un objeto de tipo no primitivo";
		error[229] = "";

		error[300] = "";
		error[301] = "El método debe retornar un valor";
		error[302] = "El valor retornado debe ser un subtipo del tipo de retorno del método";
		error[303] = "Los constructores o métodos void no pueden retornar un valor";
		error[304] = "La condición del if debe ser una expresión de tipo booleana";
		error[305] = "La condición del for debe ser una expresión de tipo booleana";
		error[306] = "La condición del while debe ser una expresión de tipo booleana";
		error[307] = "El lado izquierdo de una expresión de asignación debe ser una variable";
		error[308] = "El lado derecho de una expresión de asignación debe tener un tipo conformante con el tipo del lado izquierdo";
		error[309] = "El operador || requiere dos operandos de tipo boolean";
		error[310] = "El operador && requiere dos operandos de tipo boolean";
		error[311] = "El operador != requiere dos operandos de tipos conformantes";
		error[312] = "El operador == requiere dos operandos de tipos conformantes";
		error[313] = "Los operadores binarios de comparación requieren dos operandos de tipo int";
		error[314] = "El operador + requiere dos operandos de tipo int";
		error[315] = "El operador - requiere dos operandos de tipo int";
		error[316] = "El operador * requiere dos operandos de tipo int";
		error[317] = "El operador / requiere dos operandos de tipo int";
		error[318] = "El operador % requiere dos operandos de tipo int";
		error[319] = "El operador unario ! requiere un operando de tipo boolean";
		error[320] = "Los operadores unarios + y - requieren un operando de tipo int";
		error[321] = "Se espera el nombre de un constructor a continuación de un \"new\"";
		error[322] = "La cantidad de parámetros actuales del constructor difiere de la cantidad de parámetros formales";
		error[323] = "El tipo de los parámetros actuales del constructor difieren del tipo de los parámetros formales";
		error[324] = "No existe un método con ese nombre en la clase padre (ni en ninguno de sus ancestros)";
		error[325] = "La cantidad de parámetros actuales del método difiere de la cantidad de parámetros formales";
		error[326] = "El tipo de los parámetros actuales del método difieren del tipo de los parámetros formales";
		error[327] = "No existe un método con ese nombre en la clase actual (ni en ninguno de sus ancestros)";
		error[328] = "El identificador no puede ser resuelto a una variable (a un atributo de instancia o a un parámetro formal)";
		error[329] = "El identificador luego del \"this.\" no puede ser resuelto a una variable o método de la clase actual";
		error[330] = "No existe un método con ese nombre en la clase especificada por el identificador";
		error[331] = "No se puede invocar un método sobre un objeto null";
		error[332] = "";
		error[333] = "";
		error[334] = "";
		error[335] = "";
		error[336] = "";
		error[337] = "";
		error[338] = "";
		error[339] = "";
		error[340] = "";
		/* error[] = ""; */

		error[401] = "El literal int es demasiado grande.";
	}

	private String m(String s) {
		if (nextToken == null)
			return null;
		if (!next.equals(s))
			return null;
		// Token res = nextToken;
		advance();
		return oldToken.lexema;
	}

	private String me(String s, int e) {
		String res = m(s);
		if (res == null)
			error(e);
		return res;
	}

	private void error(int e) {
		debug("error " + e);
		int line = 0;
		if (nextToken != null)
			line = nextToken.line;
		else
			line = oldToken.line;
		if (e < 200)
			throw new SyntaxException("ERROR N°" + e + ": " + error[e]
					+ " - LINEA: " + line);
		throw new SemanticException("ERROR N°" + e + ": " + error[e]
				+ " - LINEA: " + line);
		// ver errores en tiempo de ejecucion
	}

	private boolean first(String s) {
		return first.get(s).contains(next);
	}

	private boolean peek(String s) {
		return next.equals(s);
	}

	public String analize() {
		debug("analize");
		if (nextToken == null)
			error(0);
		mStart();
		if (nextToken != null)
			error(1);

		// checkAllDefined();
		System.out.println("COMPILACIÓN EXITOSA.");
		// tc.print();
		calculateOffsetsAndCirSize();
		//System.out.println(out);
		return out;

	}

	private void mStart() {
		debug("mStart");
		mClassDef();
		mClassDefResto();
		checkClasesDeclaradas();
		mClass();
		mClassResto();
		checkAllDefined();
	}

	private void mClassDefResto() {
		debug("mClassDefResto");
		if (first("ClassDef")) {
			mClassDef();
			mClassDefResto();
		}
	}

	private void mClassResto() {
		debug("mClassResto");
		if (first("Class")) {
			mClass();
			mClassResto();
		}
	}

	private void mClassDef() {
		debug("mClassDef");
		me("classDef", 2);
		String name = me("identifier", 3);
		addClass(name, true);
		// clase = c; //poner todo con esto
		mSuper(true);
		me("{", 41);
		mDef();
		// checkConstructorVacio();
		me("}", 42);
		generateClass();
	}

	private void mDef() {
		debug("mDef");
		if (peek("identifier")) {
			String s = m("identifier");
			mCtorMethodDef(s);
		} else if (first("PrimitiveType")) {
			String type = mPrimitiveType();
			mMethodDef(type);
		} else if (peek("void")) {
			String type = m("void");
			mMethodDef(type);
		}
		checkConstructorVacio();
	}

	/**
	 * 
	 * @param s
	 *            el identificador que lei antes
	 */
	private void mCtorMethodDef(String s) { // modificar para metodos
		debug("mCtorMethodDef");
		if (first("FormalArgs")) {
			Vector<Variable> args = mFormalArgs();
			addConstructor(s, args);
			me(";", 48);
			mDef();
		} else if (first("MethodDef")) {
			mMethodDef(s);
		} else
			error(49);

	}

	private void mMethodDef(String type) {
		debug("mMethodDef");
		String name = me("identifier", 4);
		checkMethodDeclaration(name);
		Vector<Variable> args = mFormalArgs();
		addMethod(type, name, args);
		me(";", 43);
		mMethodDef2();
	}

	private void mMethodDef2() {
		if (first("MethodType")) {
			String type = mMethodType();
			mMethodDef(type);
		}
	}

	private void mClass() {
		debug("mClass");
		me("class", 5);
		String name = me("identifier", 6);
		checkClassDeclaration(name);
		mSuper(false); // CAMBIAR
		me("{", 44);
		mInClass();
		checkMethodsDefinido();
		me("}", 45);
		
		// checkConstructorVacio();
		// generateClass();
		// setear el tamaño del CIR
		// clase.cirSize = clase.parent.cirSize + clase.atributos.size();
	}

	private void mInClass() {
		debug("mInClass");
		if (peek("identifier")) {
			String n = m("identifier");
			mIsAny(n);
		} else {
			// checkConstructorDefinido();
			if (first("PrimitiveType")) {
				String type = mPrimitiveType();
				String id = me("identifier", 7);
				mIsMethodField(type, id);
			} else if (peek("void")) {
				checkConstructorDefinido();
				String type = m("void");
				String name = me("identifier", 8);
				mIsMethod(type, name);
			}
			checkConstructorDefinido();
		}

	}

	private void mIsAny(String id) {
		debug("mIsAny");
		if (first("IsCtor")) {
			mIsCtor(id);
		} else {
			// checkConstructorDefinido();
			String idd = me("identifier", 50);
			mIsMethodField(id, idd);
		}
	}

	private void mIsMethodField(String type, String name) {
		debug("mIsMethodField");
		if (first("IsMethod")) {
			checkConstructorDefinido();
			mIsMethod(type, name);
		} else if (first("IsField")) {
			mIsField(type, name);
		} else
			error(53);
	}

	private void mIsField(String type, String id) {
		debug("mIsField");

		if (tc.clases.get(type) == null && !first.get("PrimitiveType").contains(type))
			error(227);

		Vector<String> list = mVarDeclaratorListResto();
		// if (list.contains(id)) error();
		list.add(0, id);
		addVariables(type, list);
		me(";", 9);
		mInClass();
	}

	private void mIsMethod(String type, String name) {
		debug("mIsMethod");
		checkMethodTypeAndNameDefinition(type, name);
		Vector<Variable> args = mFormalArgs();
		checkMethodFormalArgsDefinition(name, args);
		metodo = clase.metodos.get(name); // establece el metodo actual
		// isConstructor = false;
		generateMethodIni();
		Attr block = mBlock();
		generateMethodFin();
		if (!block.retur && !type.equals("void"))
			error(301);
		metodo = null;
		mMethod();
	}

	private void mIsCtor(String id) {
		debug("mIsCtor");
		checkConstructorDeclaration(id);
		Vector<Variable> args = mFormalArgs();
		checkConstructorFormalArgsDeclaration(args);
		// isConstructor = true;

		generateConstructorIni();
		mBlock();
		generateConstructorFin();

		mCtorMethod();
	}

	private void mCtorMethod() {
		debug("mCtorMethod");
		if (peek("identifier")) {
			String id = m("identifier");
			mIsCtorMethod(id);
		} else if (first("PrimitiveType")) {
			String type = mPrimitiveType();
			String name = me("identifier", 7);
			mIsMethod(type, name);
		} else if (peek("void")) {
			String type = m("void");
			String name = me("identifier", 8);
			mIsMethod(type, name);
		}
	}

	private void mIsCtorMethod(String id) {
		debug("mIsCtorMethod");
		if (first("IsCtor")) {
			mIsCtor(id);
		} else {
			String name = me("identifier", 54);
			mIsMethod(id, name);
		}
	}

	/**
	 * 
	 * @param c
	 *            clase actual
	 * @param decl
	 *            true si es una declaracion, false si es una definicion
	 */
	private void mSuper(boolean decl) {
		debug("mSuper");
		if (peek("extends")) {
			m("extends");
			String name = me("identifier", 10);
			if (decl)
				addParent(name);
			else
				checkParent(name);
		}
		// else if (!decl) checkParentObject(c);
		else if (!decl && !clase.parent.name.equals("Object"))
			error(204);
	}

	private void mMethod() {
		debug("mMethod");
		if (first("MethodType")) {
			String type = mMethodType();
			String name = me("identifier", 13);
			mIsMethod(type, name);
		}
	}

	private Vector<Variable> mFormalArgs() {
		debug("mFormalArgs");
		me("(", 14);
		return mFormalArgsResto();
	}

	private Vector<Variable> mFormalArgsResto() {
		debug("mFormalArgsResto");
		Vector<Variable> res = new Vector<Variable>();
		if (peek(")"))
			m(")");
		else {
			res = mFormalArgList();
			me(")", 15);
		}
		return res;
	}

	private Vector<Variable> mFormalArgList() {
		debug("mFormalArgList");
		Variable v = mFormalArg();
		Vector<Variable> res = mFormalArgListResto();
		for (Variable vv : res)
			if (vv.name.equals(v.name))
				error(207);
		res.add(0, v);
		return res;
	}

	private Vector<Variable> mFormalArgListResto() {
		debug("mFormalArgListResto");
		Vector<Variable> res = new Vector<Variable>();
		if (peek(",")) {
			m(",");
			res = mFormalArgList();
		}
		return res;
	}

	private Variable mFormalArg() {
		debug("mFormalArg");
		String type = mType();
		String name = me("identifier", 16);
		return new Variable(name, type, false);
	}

	private String mMethodType() {
		String res = null;
		debug("mMethodType");
		if (peek("void"))
			res = m("void");
		else if (first("Type"))
			res = mType();
		else
			error(51);
		return res;
	}

	private String mType() {
		debug("mType");
		String res = null;
		if (peek("identifier")) {
			res = m("identifier");
		} else if (first("PrimitiveType")) {
			res = mPrimitiveType();
		} else
			error(52);
		return res;
	}

	private String mPrimitiveType() {
		debug("mPrimitiveType");
		String res;
		if (peek("boolean"))
			res = m("boolean");
		else if (peek("char"))
			res = m("char");
		else if (peek("int"))
			res = m("int");
		else
			res = me("String", 17); // considerar todos
		// throw new SyntaxException();
		return res;
	}

	private Vector<String> mVarDeclaratorList() {
		debug("mVarDeclaratorList");
		String id = me("identifier", 18);
		Vector<String> res = mVarDeclaratorListResto();
		// if (res.contains(id)) error();
		res.add(0, id);
		return res;
	}

	private Vector<String> mVarDeclaratorListResto() {
		debug("mVarDeclaratorListResto");
		Vector<String> res = new Vector<String>();
		if (peek(",")) {
			m(",");
			res = mVarDeclaratorList();
		}
		return res;
	}

	private Attr mBlock() {
		debug("mBlock");
		Attr block = new Attr();
		me("{", 46);
		Attr statementPart = mStatementPart();
		block.retur = statementPart.retur;
		me("}", 47);
		return block;
	}

	private Attr mStatementPart() {
		debug("mStatementPart");
		Attr statementPart = new Attr();
		if (first("Statement")) {
			Attr statement = mStatement();
			Attr statementPart1 = mStatementPart();
			statementPart.retur = statement.retur || statementPart1.retur;
		}
		return statementPart;
	}

	private Attr mStatement() {
		debug("mStatement");
		Attr statement = new Attr();
		if (peek(";"))
			m(";");
		else if (peek("if")) {
			m("if");
			me("(", 19);
			Attr expression = mExpression();
			
			String l1 = "$ifLabel$"+(++indexIf);
			String l2 = "$ifLabel$"+(++indexIf);
			gen("\nBF "+l1); //branch false			
			
			if (!expression.type.equals("boolean")) error(304);
			me(")", 20);
			Attr statement1 = mStatement();
			
			gen("JUMP "+l2);
			gen(l1,"NOP");	
			
			Attr ifResto = mIfResto();
			statement.retur = statement1.retur && ifResto.retur;
			
			gen(l2,"NOP");
			
		} else if (peek("return")) {
			m("return");
			Attr returnResto = mReturnResto();
			if (metodo != null && !metodo.type.equals("void")) { // es un metodo no void
				if (!subType(returnResto.returnType, metodo.type)) error(302);
				statement.retur = true;
				
				generateMethodFin();
			} 
			else { // es un metodo void o un constructor
				if (!returnResto.returnType.equals("void")) error(303);
				
				if (metodo == null) generateConstructorFin();
				else generateMethodFin();
			}
			me(";", 21);
		} else if (first("Block")) {
			Attr block = mBlock();
			statement.retur = block.retur;
		} else if (first("Expression")) {
			Attr expression = mExpression();
			me(";", 22);
			
			if (!expression.type.equals("void")) gen("POP"); //desapilo el resultado de la expresión en caso de no ser de tipo void
		} else if (peek("for")) {
			m("for");
			me("(", 23);
			Attr expression = mExpression();
			if (!expression.type.equals("void")) gen("POP"); //desapilo el resultado de la expresión en caso de no ser de tipo void
			
			
			String l1 = "$forLabel$"+(++indexFor);
			String l2 = "$forLabel$"+(++indexFor);
			String l3 = "$forLabel$"+(++indexFor);
			String l4 = "$forLabel$"+(++indexFor);
			
			gen("\n"+l1,"NOP");
			
			me(";", 24);
			Attr expression2 = mExpression();
			if (!expression2.type.equals("boolean")) error(305);
			
			gen("BF "+l2);			
			gen("JUMP "+l4);
			gen(l3,"NOP");
			
			me(";", 25);
			Attr expression3 = mExpression();
			me(")", 26);
			if (!expression.type.equals("void")) gen("POP"); //desapilo el resultado de la expresión en caso de no ser de tipo void
			gen("JUMP "+l1);
			gen(l4,"NOP");
			
			Attr statement1 = mStatement();
			statement.retur = statement1.retur;
			
			gen("JUMP "+l3);
			gen(l2,"NOP");
			
		} else {
			me("while", 27);
			me("(", 28);
			
			String l1 = "$whileLabel$"+(++indexWhile);
			String l2 = "$whileLabel$"+(++indexWhile);
			
			gen("\n"+l1,"NOP");
			
			Attr expression = mExpression();
			
			gen("BF "+l2);
			
			if (!expression.type.equals("boolean")) error(306);
			me(")", 29);
			Attr statement1 = mStatement();
			
			gen("JUMP " + l1);
			gen(l2,"NOP");	
					
			statement.retur = statement1.retur;
		}
		return statement;

	}

	private Attr mIfResto() {
		debug("mIfResto");
		Attr ifResto = new Attr();
		if (peek("else")) {
			m("else");
			Attr statement = mStatement();
			ifResto.retur = statement.retur;
		} else {
			ifResto.retur = false;
		}
		return ifResto;
	}

	private Attr mReturnResto() {
		debug("mReturnResto");
		Attr returnResto = new Attr();
		if (first("Expression")) {
			Attr expression = mExpression();
			returnResto.returnType = expression.type;
		} else {
			returnResto.returnType = "void";
		}
		return returnResto;
	}

	private Attr mExpression() {
		debug("mExpression");

		gen("");

		Attr expressionOR = mExpressionOR();

		Attr expressionResto = new Attr();
		expressionResto.v = expressionOR.v;
		expressionResto.type = expressionOR.type;
		expressionResto.l_value = expressionOR.l_value;
		expressionResto.id = expressionOR.id;
		// if (expressionResto.v != null)
		// pp("expressionResto.v "+expressionResto.v.name+ " " +
		// expressionResto.v.esAtributo);
		mExpressionResto(expressionResto);
		Attr expression = new Attr();
		expression.type = expressionOR.type;
		expression.l_value = expressionOR.l_value && expressionResto.l_value;
		expression.v = expressionResto.v;

		/*if (desapilar)
			gen("POP"); // si no estoy del lado derecho, desapilo el resultado
		desapilar = false;*/

		return expression;
	}

	private void mExpressionResto(Attr expressionResto) {
		debug("mExpressionResto");

		if (peek("=")) {
			m("=");

			gen("POP"); //desapilo el lado izquierdo
			if (!expressionResto.l_value)
				error(307);

			Attr expression = mExpression();
			//desapilar = true;

			if (!subType(expression.type, expressionResto.type))
				error(308);

			generateAsignacion(expressionResto.v);

		} else {
			expressionResto.l_value = true;
		}
		debug("fin expresionResto");
	}

	private Attr mExpressionOR() {
		debug("mExpressionOR");
		Attr expressionOR = new Attr();
		Attr expressionOR2 = new Attr();
		Attr expressionAND = mExpressionAND();
		expressionOR2.type = expressionAND.type;
		mExpressionOR2(expressionOR2);

		expressionOR.type = expressionOR2.type;
		expressionOR.l_value = expressionAND.l_value && expressionOR2.l_value;
		expressionOR.v = expressionAND.v;
		expressionOR.id = expressionAND.id;

		// if (expressionOR.v != null) pp("expressionOR.v "+expressionOR.v.name+
		// " " + expressionOR.v.esAtributo);
		return expressionOR;
	}

	private void mExpressionOR2(Attr expressionOR2) {
		debug("mExpressionOR2");
		if (peek("||")) {
			m("||");
			
			//cortocircuito
			String l1 = "$cortoLabel$"+(++indexIf);
			gen("DUP");
			gen("BT "+l1); //branch false
			
			Attr expressionAND = mExpressionAND();

			gen("OR");

			if (!expressionOR2.type.equals("boolean")
					|| !expressionAND.type.equals("boolean"))
				error(309);
			Attr expressionOR21 = new Attr();
			expressionOR21.type = "boolean";
			mExpressionOR2(expressionOR21);
			expressionOR2.type = expressionOR21.type;
			gen(l1,"NOP");
		} 
		else {
			expressionOR2.l_value = true;
		}
	}

	private Attr mExpressionAND() {
		debug("mExpressionAND");
		Attr expressionCompare = mExpressionCompare();
		Attr expressionAND2 = new Attr();
		Attr expressionAND = new Attr();
		expressionAND2.type = expressionCompare.type;
		mExpressionAND2(expressionAND2);
		expressionAND.type = expressionAND2.type;
		expressionAND.l_value = expressionCompare.l_value && expressionAND2.l_value;
		expressionAND.v = expressionCompare.v;
		expressionAND.id = expressionCompare.id;

		return expressionAND;
	}

	private void mExpressionAND2(Attr expressionAND2) {
		debug("mExpressionAND2");
		
		if (peek("&&")) {
			m("&&");
						
			//cortocircuito
			String l1 = "$cortoLabel$"+(++indexIf);
			gen("DUP");
			gen("BF "+l1); //branch false
			
			Attr expressionCompare = mExpressionCompare();

			gen("AND");

			if (!expressionAND2.type.equals("boolean") || !expressionCompare.type.equals("boolean")) error(310);
			Attr expressionAND21 = new Attr();
			expressionAND21.type = "boolean";
			mExpressionAND2(expressionAND21);
			expressionAND2.type = expressionAND21.type;
			
			gen(l1,"NOP");
			
		} 
		else {
			expressionAND2.l_value = true;
		}
			
		
	}

	private Attr mExpressionCompare() {
		debug("mExpressionCompare");
		Attr expressionArit = mExpressionArit();
		Attr expressionCompare2 = new Attr();
		expressionCompare2.type = expressionArit.type;
		mExpressionCompare2(expressionCompare2);
		Attr expressionCompare = new Attr();
		expressionCompare.type = expressionCompare2.type;
		expressionCompare.l_value = expressionArit.l_value
				&& expressionCompare2.l_value;
		expressionCompare.v = expressionArit.v;
		expressionCompare.id = expressionArit.id;
		return expressionCompare;
	}

	private void mExpressionCompare2(Attr expressionCompare2) {
		debug("mExpressionCompare2");
		if (peek("!=")) {
			m("!=");
			Attr expressionArit = mExpressionArit();

			gen("NE");

			if (!conformante(expressionCompare2.type, expressionArit.type))
				error(311);
			expressionCompare2.type = "boolean";
		} else if (peek("==")) {
			m("==");
			Attr expressionArit = mExpressionArit();

			gen("EQ");

			if (!conformante(expressionCompare2.type, expressionArit.type))
				error(312);
			expressionCompare2.type = "boolean";
		} else {
			expressionCompare2.l_value = true;
		}
	}

	private Attr mExpressionArit() {
		debug("mExpressionArit");
		Attr expressionSum = mExpressionSum();
		Attr expressionAritResto = new Attr();
		expressionAritResto.type = expressionSum.type;
		mExpressionAritResto(expressionAritResto);
		Attr expressionArit = new Attr();
		expressionArit.type = expressionAritResto.type;
		expressionArit.l_value = expressionSum.l_value
				&& expressionAritResto.l_value;
		expressionArit.v = expressionSum.v;
		expressionArit.id = expressionSum.id;
		return expressionArit;
	}

	private Attr mExpressionAritResto(Attr expressionAritResto) {
		debug("mExpressionAritResto");
		// Attr expressionAritResto = new Attr();
		Attr expressionSum;
	//	String op;
		if (peek("<=")) {
			m("<=");
			expressionSum = mExpressionSum();
			gen("LE");
		} else if (peek(">=")) {
			m(">=");
			expressionSum = mExpressionSum();
			gen("GE");
		} else if (peek("<")) {
			m("<");
			expressionSum = mExpressionSum();
			gen("LT");
		} else if (peek(">")) {
			m(">");
			expressionSum = mExpressionSum();
			gen("GT");
		} else {
			expressionAritResto.l_value = true;
			return expressionAritResto;
		}
		if (!expressionAritResto.type.equals("int")
				|| !expressionSum.type.equals("int"))
			error(313);
		expressionAritResto.type = "boolean";
		return expressionAritResto;
	}

	private Attr mExpressionSum() {
		debug("mExpressionSum");
		Attr term = mTerm();
		Attr expressionSum2 = new Attr();
		expressionSum2.type = term.type;
		mExpressionSum2(expressionSum2);
		Attr expressionSum = new Attr();
		expressionSum.type = expressionSum2.type;
		expressionSum.l_value = term.l_value && expressionSum2.l_value;
		expressionSum.v = term.v;
		// if (expressionSum.v != null)
		// pp("expressionSum.v "+expressionSum.v.name+ " " +
		// expressionSum.v.esAtributo);
		expressionSum.id = term.id;
		return expressionSum;
	}

	private void mExpressionSum2(Attr expressionSum2) {
		debug("mExpressionSum2");
		if (peek("+")) {
			m("+");
			Attr term = mTerm();

			gen("ADD");

			if (!expressionSum2.type.equals("int") || !term.type.equals("int"))
				error(314);
			Attr expressionSum21 = new Attr();
			expressionSum21.type = "int";
			mExpressionSum2(expressionSum21);
			expressionSum2.type = expressionSum21.type;
		} else if (peek("-")) {
			m("-");
			Attr term = mTerm();

			gen("SUB");

			if (!expressionSum2.type.equals("int") || !term.type.equals("int"))
				error(315);
			Attr expressionSum21 = new Attr();
			expressionSum21.type = "int";
			mExpressionSum2(expressionSum21);
			expressionSum2.type = expressionSum21.type;
		} else {
			expressionSum2.l_value = true;
		}
	}

	private Attr mTerm() {
		debug("mTerm");

		Attr factor = mFactor();
		Attr term2 = new Attr();
		term2.type = factor.type;
		mTerm2(term2);

		Attr term = new Attr();
		term.type = term2.type;
		term.l_value = factor.l_value && term2.l_value;
		term.v = factor.v;

		// if (term.v != null) pp("term.v "+term.v.name+ " " +
		// term.v.esAtributo);
		term.id = factor.id;

		return term;
	}

	private void mTerm2(Attr term2) {
		debug("mTerm2");
		if (peek("%")) {
			m("%");
			Attr factor = mFactor();

			generateDivisionByZeroCheck();
			gen("MOD");

			if (!term2.type.equals("int") || !factor.type.equals("int"))
				error(318);
			Attr term21 = new Attr();
			term21.type = "int";
			mTerm2(term21);
			term2.type = term21.type;
		} else if (peek("/")) {
			m("/");
			Attr factor = mFactor();

			generateDivisionByZeroCheck();
			gen("DIV");

			if (!term2.type.equals("int") || !factor.type.equals("int"))
				error(317);
			Attr term21 = new Attr();
			term21.type = "int";
			mTerm2(term21);
			term2.type = term21.type;
		} else if (peek("*")) {
			m("*");
			Attr factor = mFactor();

			gen("MUL");

			if (!term2.type.equals("int") || !factor.type.equals("int"))
				error(316);
			Attr term21 = new Attr();
			term21.type = "int";
			mTerm2(term21);
			term2.type = term21.type;
		} else {
			term2.l_value = true;
		}
	}

	private Attr mFactor() {
		debug("mFactor");
		Attr factor = new Attr();
		if (first("UnaryOp")) {
			String unaryOp = mUnaryOp();
			Attr expression = mExpression();
			if (unaryOp.equals("!")) {
				if (!expression.type.equals("boolean"))
					error(319);
				factor.type = "boolean";

				gen("NOT");

			} else {
				if (!expression.type.equals("int"))
					error(320);
				factor.type = "int";

				if (unaryOp.equals("-"))
					gen("NEG");

			}
		} else if (first("Primary")) {
			Attr primary = mPrimary();
			factor.type = primary.type;
			factor.l_value = primary.l_value;
			factor.v = primary.v;
			// if (factor.v != null) pp("factor.v "+factor.v.name+ " " +
			// factor.v.esAtributo);
			factor.id = primary.id;
			// podria hacer directamnete algo asi como factor = primary.clon();
		} else
			error(55);
		return factor;
	}

	private String mUnaryOp() {
		debug("mUnaryOp");
		String res;
		if (peek("!"))
			res = m("!");
		else if (peek("+"))
			res = m("+");
		else
			res = me("-", 30);
		return res;
	}

	private Attr mPrimary() {
		debug("mPrimary");
		Attr primary = new Attr();
		if (first("Literal")) {
			Attr literal = mLiteral();
			primary.type = literal.type;
		} else if (peek("identifier")) {
			String id = m("identifier");
			Attr primaryResto = new Attr();
			// primaryResto.type = buscarTipo(id);
			primaryResto.id = id;
			mPrimaryResto(primaryResto);
			primary.type = primaryResto.type;
			primary.l_value = primaryResto.l_value;
			primary.v = primaryResto.v;
			// pp("primary.v "+primary.v.name+ " " + primary.v.esAtributo);
			primary.id = primaryResto.id;
		} else if (peek("this")) {
			m("this");
			Attr primaryResto2 = new Attr();
			mPrimaryResto2(primaryResto2);
			// primary.type = clase.name;
			primary.type = primaryResto2.type;
			primary.l_value = primaryResto2.l_value;
			primary.v = primaryResto2.v;
			primary.id = primaryResto2.id;
		} else if (peek("(")) {
			m("(");
			Attr expression = mExpression();
			// if (expression.v != null) pp("expression.v "+expression.v.name+
			// " " + expression.v.esAtributo);
			me(")", 31);
			Attr primary2 = new Attr();
			primary2.type = expression.type;
			mPrimary2(primary2);
			primary.l_value = expression.l_value && primary2.l_value;
			primary.type = primary2.type;
			primary.v = expression.v;
		} else if (peek("new")) {
			m("new");
			String id = me("identifier", 32);
			if (!esConstructor(id))
				error(321);

			generateNew(id);

			Attr actualArgs = mActualArgs();
			if (cantidadParametros(id, id) != actualArgs.paramsTypes.size())
				error(322);
			if (!chequeoTipos(id, id, actualArgs.paramsTypes))
				error(323);

			generateNew2(id);

			Attr primary2 = new Attr();
			primary2.type = id;
			// System.out.println(primary2.type);
			mPrimary2(primary2);
			primary.type = primary2.type;
		} else {
			me("super", 33); // considerar
			me(".", 34);
			String id = me("identifier", 35);
			if (returnType(clase.parent.name, id) == null)
				error(324);

			generateSuperCallIni(id);

			Attr actualArgs = mActualArgs();
			if (cantidadParametros(clase.parent.name, id) != actualArgs.paramsTypes
					.size())
				error(325);
			if (!chequeoTipos(clase.parent.name, id, actualArgs.paramsTypes))
				error(326);

			generateSuperCallFin(id);

			Attr primary2 = new Attr();
			primary2.type = returnType(clase.parent.name, id);
			mPrimary2(primary2);
			primary.type = primary2.type;
		}

		return primary;
	}

	private void mPrimaryResto(Attr primaryResto) {
		debug("mPrimaryResto");
		if (first("ActualArgs")) {
			if (returnType(clase.name, primaryResto.id) == null)
				error(327);

			gen(".CODE");
			gen("LOAD 3");
			generateMethodCallIni(clase.name, primaryResto.id);

			Attr actualArgs = mActualArgs();
			if (cantidadParametros(clase.name, primaryResto.id) != actualArgs.paramsTypes
					.size())
				error(325);
			if (!chequeoTipos(clase.name, primaryResto.id,
					actualArgs.paramsTypes))
				error(326);

			generateMethodCallFin(clase.name, primaryResto.id);

			Attr primary2 = new Attr();
			primary2.esMetodo = true;
			primary2.type = returnType(clase.name, primaryResto.id);
			mPrimary2(primary2);
			primaryResto.type = primary2.type;
			primaryResto.l_value = primary2.l_value;
		} else {// if (first("Primary2")) {
			if (variableType(clase.name, primaryResto.id, !primaryResto.esThis) == null)
				error(328);
			Attr primary2 = new Attr();
			primaryResto.v = variableType(clase.name, primaryResto.id,
					!primaryResto.esThis);

			// pp("primaryResto.v "+primaryResto.v.name+ " " +
			// primaryResto.v.esAtributo);

			generateVariable(primaryResto.v);

			primary2.type = primaryResto.v.type;
			primary2.id = primaryResto.id;
			mPrimary2(primary2);
			primaryResto.type = primary2.type;
			primaryResto.l_value = primary2.l_value;
			primaryResto.id = primary2.id;

			// if (primaryResto.l_value) generateVariable(primaryResto.v);
		}
	}

	private void mPrimaryResto2(Attr primaryResto2) {
		debug("mPrimaryResto2");
		if (peek(".")) {
			me(".", 56);
			String id = me("identifier", 36);
			if (returnType(clase.name, id) == null && variableType(clase.name, id, false) == null)
				error(329);
			Attr primaryResto = new Attr();
			if (returnType(clase.name, id) != null) {
				primaryResto.type = returnType(clase.name, id);
			} else {
				primaryResto2.v = variableType(clase.name, id, false);
				primaryResto.type = primaryResto2.v.type;
			}
			primaryResto.esThis = true;
			primaryResto.id = id;
			mPrimaryResto(primaryResto);
			primaryResto2.type = primaryResto.type;
			primaryResto2.l_value = primaryResto.l_value;
			primaryResto2.id = primaryResto.id;
		} else {
			primaryResto2.type = clase.name;
		}
	}

	private void mPrimary2(Attr primary2) {
		debug("mPrimary2");

		if (peek(".")) {
			m(".");
			String id = me("identifier", 37);
			if (primary2.type.equals("null")) error(331);
			if (returnType(primary2.type, id) == null)
				error(330);

			generateMethodCallIni(primary2.type, id);
			Attr actualArgs = mActualArgs();
			if (cantidadParametros(primary2.type, id) != actualArgs.paramsTypes
					.size())
				error(325);

			if (!chequeoTipos(primary2.type, id, actualArgs.paramsTypes))
				error(326);

			generateMethodCallFin(primary2.type, id);

			Attr primary21 = new Attr();
			primary21.esMetodo = true;
			primary21.type = returnType(primary2.type, id);
			mPrimary2(primary21);
			primary2.type = primary21.type;
			primary2.l_value = primary21.l_value;
		} else {
			if (!primary2.esMetodo)
				primary2.l_value = true;
		}
		debug("fin primary2");

	}

	private Attr mLiteral() {
		debug("mLiteral");
		Attr literal = new Attr();
		if (peek("null")) {
			m("null");
			//literal.type = "Object";
			literal.type = "null";
			gen("PUSH 0");
			
		} else if (peek("true")) {
			m("true");
			literal.type = "boolean";

			gen("PUSH 1");

		} else if (peek("false")) {
			m("false");
			literal.type = "boolean";

			gen("PUSH 0");

		} else if (peek("intLiteral")) {
			String i = m("intLiteral");
			checkIntegerSize(i);
			literal.type = "int";

			gen("PUSH " + i);

		} else if (peek("charLiteral")) {
			String c = m("charLiteral");
			literal.type = "char";
			
			gen("PUSH "+ c.substring(0, 3));
			
		} else {
			String s = me("stringLiteral", 38);
			literal.type = "String";
			
			gen(".DATA");
			String l = "$stringLabel$" +(indexString++);
			gen(l,"DW "+s+",0");
			gen(".CODE");
			gen("PUSH "+l);
			
		}
		return literal;
	}

	private Attr mActualArgs() {
		debug("mActualArgs");
		Attr actualArgs = new Attr();
		me("(", 39);
		Attr exprListPart = mExprListPart();
		actualArgs.paramsTypes = exprListPart.paramsTypes;
		// actualArgs.parametros = exprListPart.parametros;
		me(")", 40);
		return actualArgs;
	}

	private Attr mExprListPart() {
		debug("mExprListPart");
		if (first("ExprList"))
			return mExprList();
		else {
			return new Attr();
		}
	}

	private Attr mExprList() {
		debug("mExprList");
		Attr exprList = new Attr();
		Attr expression = mExpression();

		gen("SWAP");

		Attr exprListResto = mExprListResto();
		exprList.paramsTypes = exprListResto.paramsTypes;
		exprList.paramsTypes.add(0, expression.type);
		return exprList;
	}

	private Attr mExprListResto() {
		debug("mExprListResto");
		if (peek(",")) {
			m(",");
			return mExprList();
		} else {
			return new Attr();
		}
	}

	private void debug(String s) {
		if (debug)
			System.out.println(s);
	}

	// SEMANTICO

	private void initClassTable() {
		tc = new TablaDeClases();
		// agregar las clases por defecto
	}

	/**
	 * Agrega a la table de clases una nueva clase
	 * 
	 * @param p
	 *            true si fue declarada, false si todavía no está declarada
	 */
	private void addClass(String name, boolean p) {
		debug("addClass - name = " + name + " p = " + p);
		// chequeos de error : si ya existe una clase con ese nombre, ERROR
		if (tc.get(name) != null && tc.get(name).declarada)
			error(201);

		Clase res = new Clase(name, p);
		tc.add(res);
		if (p)
			clase = res;
	}

	private void addParent(String name) {
		clase.parent = tc.get(name);
		// si no existe la clase padre, ERROR
		if (clase.parent == null)
			error(202);
		if (clase.parent == clase)
			error(206);

		// creo el allMetodos
		clase.allMetodos = new HashMap<String, Metodo>(clase.parent.allMetodos);
	}

	private void checkClassDeclaration(String name) {
		clase = tc.get(name);
		if (clase == null)
			error(203);
		if (clase.definida)
			error(201);
		clase.definida = true;
	}

	private void checkParent(String name) {
		if (!clase.parent.name.equals(name))
			error(204);
	}

	/**
	 * Chequea que todas las clases declaradas hayan sido definidas
	 */
	private void checkAllDefined() {
		debug("checkAllDefined");
		for (Clase c : tc.clases.values())
			if (!c.definida)
				error(205);
	}

	/*
	 * private void checkParentObject(Clase c) { if (c.parent.name) error(204);
	 * }
	 */

	private void addConstructor(String n, Vector<Variable> args) {
		if (!n.equals(clase.name))
			error(208);
		if (clase.constructor != null)
			error(209); // doble constructor
		// Constructor cc= new Constructor(args);
		clase.constructor = new Constructor(args);
		clase.constructor.etiqueta = "Ctor" + "$" + clase.name;

		// calculo el offset de los parámetros
		int si = args.size();
		for (int i = 0; i < si; i++)
			args.elementAt(i).offset = si + 3 - i;

		for (Variable v : args)
			if (!primitiveType(v.type) && tc.clases.get(v.type) == null)
				addClass(v.type, false);
	}

	private void checkConstructorDeclaration(String id) {
		debug("checkConstructorDeclaration");
		if (!id.equals(clase.name))
			error(208);
		// if (clase.constructor == null) error(214);
		if (!clase.constructor.declarado)
			error(214);
		if (clase.constructor.definido)
			error(209);

		clase.constructor.definido = true;
	}

	private void checkConstructorFormalArgsDeclaration(Vector<Variable> args) {
		Vector<Variable> p = clase.constructor.parametros;
		if (args.size() != p.size())
			error(210);
		for (int i = 0; i < p.size(); i++)
			if (!args.elementAt(i).type.equals(p.elementAt(i).type)
					|| !args.elementAt(i).name.equals(p.elementAt(i).name))
				error(210);
	}

	/**
	 * Si la clase no tiene constructor, le asigna uno vacío
	 */
	private void checkConstructorVacio() {
		if (clase.constructor == null)
			clase.constructor = TablaDeClases.CONSTRUCTOR_VACIO;
	}

	/**
	 * Chequea que el constructor declarado haya sido definido
	 */
	private void checkConstructorDefinido() {
		debug("checkConstructorDefinido");
		// checkConstructorVacio();
		if (!clase.constructor.definido)
			error(211);
	}

	/**
	 * Agrega variables a la clase actual
	 * 
	 * @param type
	 *            el tipo de las variables
	 * @param names
	 *            los nombres de las variables
	 */
	private void addVariables(String type, Vector<String> names) {
		// int pos = clase.parent.cirSize + clase.atributos.size(); // ver
		for (String n : names) {
			if (clase.atributos.get(n) != null)
				error(212);
			if (clase.name.equals(n))
				error(213);
			if (clase.metodos.containsKey(n))
				error(221);

			// chequeo contra los métodos heredados
			Clase c = clase.parent;
			while (c != null) {
				if (c.metodos.containsKey(n))
					error(221);
				c = c.parent;
			}

			Variable v = new Variable(n, type, true);
			v.etiqueta = "11" + clase.name + "-" + n + "11";
			// v.offset = pos++; // seteo el offset del atributo de instancia en
			// el CIR
			clase.atributos.put(n, v);
			// pp(n+" "+v.offset);
		}
	}

	/**
	 * Agrega un método a la clase actual (desde el classDef)
	 * 
	 * @param type
	 * @param name
	 * @param args
	 */
	private void addMethod(String type, String name, Vector<Variable> args) {
		debug("addMethod - type = " + type + " name = " + name);
		checkSobreescritura(type, name, args);
		Metodo method = new Metodo(type, name, args);
		clase.metodos.put(name, method);
		// Agrego las clases que no hayan sido declaradas hasta ahora
		if (!primitiveType(type) && tc.clases.get(type) == null)
			addClass(type, false);
		for (Variable v : args)
			if (!primitiveType(v.type) && tc.clases.get(v.type) == null)
				addClass(v.type, false);

		// calcular offset y etiqueta
		if (clase.allMetodos.containsKey(name)) { // redefinición
			method.offset = clase.allMetodos.get(name).offset;
		} else {
			method.offset = clase.allMetodos.size();
		}
		method.etiqueta = "l" + name + "$" + clase.name;
		clase.allMetodos.put(name, method);

		// calculo el offset de los parámetros
		int n = args.size();
		for (int i = 0; i < n; i++)
			args.elementAt(i).offset = n + 3 - i;

	}

	private void checkAncestorsName(String name, Clase c) {
		Clase padre = c.parent;
		if (padre != null) {
			if (padre.name.equals(name))
				error(215);
			checkAncestorsName(name, padre);
		}
	}

	/**
	 * Determina si t es un tipo primitivo o un void
	 * 
	 * @param t
	 * @return
	 */
	private boolean primitiveType(String t) {
		String types[] = { "boolean", "char", "int", "String", "void" };
		for (String s : types)
			if (t.equals(s))
				return true;
		return false;
	}

	/**
	 * Al momento de declarar un método, se realizan algunos chequeos
	 * 
	 * @param name
	 */
	private void checkMethodDeclaration(String name) {
		debug("checkMethodDeclaration");
		debug("name = " + name);

		// chequeo que el nombre no coincida con el nombre de la clase ni otro
		// metodo
		if (name.equals(clase.name))
			error(215);
		if (clase.metodos.containsKey(name))
			error(216);
		// chequeo que el nombre no coincida con una clase ancestro
		checkAncestorsName(name, clase);
	}

	/**
	 * Chequea que la definición del tipo y nombre del método se correspondan
	 * con su declaración
	 * 
	 * @param type
	 * @param name
	 */
	private void checkMethodTypeAndNameDefinition(String type, String name) {
		debug("checkMethodTypeAndNameDefinition - type = " + type + " name = "
				+ name);
		Metodo m = clase.metodos.get(name);
		if (m == null)
			error(217);
		if (m.definido)
			error(216);
		if (!m.type.equals(type))
			error(218);
		m.definido = true;
	}

	/**
	 * Chequea que la definición de los argumentos formales del método se
	 * correspondan con su declaración
	 * 
	 * @param name
	 * @param args
	 */
	private void checkMethodFormalArgsDefinition(String name,
			Vector<Variable> args) {
		debug("checkMethodFormalArgsDefinition");
		Vector<Variable> p = clase.metodos.get(name).parametros;
		if (args.size() != p.size())
			error(219);
		for (int i = 0; i < p.size(); i++)
			if (!args.elementAt(i).type.equals(p.elementAt(i).type)
					|| !args.elementAt(i).name.equals(p.elementAt(i).name))
				error(219);
	}

	/**
	 * Chequea que todos los métodos declarados hayan sido definidos
	 */
	private void checkMethodsDefinido() {
		debug("checkMethodsDefinido");
		for (Metodo m : clase.metodos.values())
			if (!m.definido)
				error(220);
	}

	/**
	 * Chequea que todas las clases utilizadas en los classDef fueron declaradas
	 * y que la clase Main fue declarada
	 */
	private void checkClasesDeclaradas() {
		for (Clase c : tc.clases.values())
			if (!c.declarada)
				error(222);
		checkMain();
	}

	/**
	 * Chequea que la sobreescritura sea correcta (desde el classDef)
	 */
	private void checkSobreescritura(String type, String name,
			Vector<Variable> args) {
		Clase c = clase.parent;
		while (c != null) {
			Metodo m = c.metodos.get(name);
			if (m != null) {
				if (!m.type.equals(type))
					error(223);
				if (m.parametros.size() != args.size())
					error(224);
				for (int i = 0; i < args.size(); i++)
					if (!args.elementAt(i).type.equals(m.parametros
							.elementAt(i).type))
						error(224); // ver si chequeo el nombre tambien

			}
			c = c.parent;
		}
	}

	/**
	 * Chequea que la clase Main esté declarada como corresponde
	 */
	private void checkMain() {
		Clase c = tc.get("Main");
		if (c == null)
			error(225);
		if (!c.constructor.declarado)
			error(226);
		if (c.constructor.parametros.size() > 0)
			error(226);
	}

	/*
	 * private Attr eExpression(Attr expressionOr,Attr expressionResto) {
	 * expressionResto.put("inh", ) }
	 */

	private void checkIntegerSize(String i) {
		if (i.length() > 5)
			error(401);
	}

	// CHEQUEOS DE TIPOS

	/**
	 * Chequea si un tipo es subtipo de otro
	 * 
	 * @param t1
	 * @param t2
	 * @return true si t1 es subtipo de t2, false en caso contrario
	 */
	private boolean subType(String t1, String t2) {
		
		if (t1.equals("null")) return true; // null es subtipo de cualquier cosa
		// pp("subType " + t1 + " " + t2);
		// System.out.println("subType "+t1+" "+t2);
		if (t1.equals(t2)) return true;

		// si t1 es un tipo primitivo y es distinto de t2, entonces retorna falso o si t1 es void
		if (first.get("PrimitiveType").contains(t1) || t1.equals("void"))
			return false;
		if (!t1.equals("Object"))
			return subType(tc.clases.get(t1).parent.name, t2);
		return false;
	}

	/**
	 * Determina si dos tipos son conformantes, es decir, si uno es subtipo del
	 * otro
	 * 
	 * @param t1
	 * @param t2
	 * @return true si t1 es subtipo de t2 o t2 es subtipo de t1, false en caso
	 *         contrario
	 */
	private boolean conformante(String t1, String t2) {
		return subType(t1, t2) || subType(t2, t1);
	}

	/**
	 * Determina si el id dado es el nombre de alguna clase
	 * 
	 * @param id
	 * @return
	 */
	private boolean esConstructor(String id) {
		return tc.clases.containsKey(id);
	}

	/**
	 * Retorna la cantidad de parámetros del método o constructor id de la clase
	 * c (o de alguno de sus ancestros)
	 * 
	 * @param c
	 * @param id
	 * @return -1 si no existe ese método o constructor
	 */
	private int cantidadParametros(String c, String id) {
		debug("cantidadParametros " + c + " " + id);
		if (c.equals(id)) // id es un constructor
			return tc.clases.get(id).constructor.parametros.size();
		Metodo m = tc.clases.get(c).metodos.get(id);
		if (m != null)
			return m.parametros.size();
		if (c.equals("Object"))
			return -1;
		return cantidadParametros(tc.clases.get(c).parent.name, id);
	}

	/**
	 * Chequea que los tipos de los parámetros sean correctos. Puede ser en la
	 * clase c o en un ancestro Se asume que el método o constructor existen en
	 * la clase o en alguno de sus ancestros
	 * 
	 * @param c
	 *            clase
	 * @param id
	 *            método o constructor
	 * @param types
	 *            tipos de los parámetros
	 * @return
	 */
	private boolean chequeoTipos(String c, String id, Vector<String> types) {
		// pp("chequeoTipos "+c+" "+id);
		Vector<Variable> param;
		if (c.equals(id)) // id es un constructor
			param = tc.clases.get(c).constructor.parametros;
		else {// id es un método
			Metodo m = tc.clases.get(c).metodos.get(id);
			if (m == null)
				return chequeoTipos(tc.clases.get(c).parent.name, id, types);
			param = m.parametros;
		}

		for (int i = 0; i < types.size(); i++)
			// if (!types.elementAt(i).equals(param.elementAt(i).type)) return
			// false;
			if (!subType(types.elementAt(i), param.elementAt(i).type))
				return false;

		return true;
	}

	/**
	 * Retorna el tipo de retorno del método id de la clase c o de alguno de sus
	 * ancestros
	 * 
	 * @param c
	 * @param id
	 * @return null en caso de que no exista el método id en la clase c ni en
	 *         ninguno de sus ancestros
	 */
	private String returnType(String c, String id) {
		debug("returnType " + c + " " + id);
		if (first.get("PrimitiveType").contains(c) || c.equals("void"))
			error(228);
		Metodo m = tc.get(c).metodos.get(id);
		if (m == null) {
			if (c.equals("Object"))
				return null;
			return returnType(tc.get(c).parent.name, id);
		}
		return m.type;
	}

	/**
	 * Retorna una Variable que representa la variable id de la clase c o del
	 * parametro formal (de método o constructor) Primero trata de buscar un
	 * parámetro formal y si no encuentra, busca un atributo de instancia
	 * 
	 * @param c
	 * @param id
	 * @param param
	 *            true si tengo que buscar en los parámetros también
	 * @return null en caso de que no exista la variable id en la clase c
	 */
	private Variable variableType(String c, String id, boolean param) {
		// pp("variableType "+c+" "+id+" "+param);
		// 1. parametros formales (del metodo o constructor)
		// 2. atributos de instancia

		if (param) {
			if (metodo == null) { // estoy en un constructor
				int index = pos(clase.constructor.parametros, id);
				if (index > -1)
					return clase.constructor.parametros.elementAt(index);
			} else { // estoy en un metodo
				int index = pos(metodo.parametros, id);
				if (index > -1)
					return metodo.parametros.elementAt(index);
			}
		}

		Variable v = tc.get(c).atributos.get(id);
		return v;
	}

	/**
	 * Retorna el indice de la variable de nombre id en el vector de variables
	 * param
	 * 
	 * @param param
	 * @param id
	 * @return -1 si no está
	 */
	private int pos(Vector<Variable> param, String id) {
		// pp("pos "+id);
		int res = -1;
		for (int i = 0; i < param.size(); i++) {
			if (param.elementAt(i).name.equals(id))
				res = i;
		}
		return res;
	}

	// GENERACIÓN DE CÓDIGO

	/**
	 * Genera el código correspondiente a la VT
	 */
	private void generateClass() {

		// generar VT
		String cod = "";

		int n = clase.allMetodos.size();
		gen("\n; clase " + clase.name);
		gen(".DATA");

		if (n > 0) {

			cod = "DW ";
			String sep = "";
			String a[] = new String[n];
			for (Metodo m : clase.allMetodos.values())
				a[m.offset] = m.etiqueta;
			// gen("VT" + clase.name, cod);
			for (int i = 0; i < n; i++) {
				cod = cod + sep + a[i];
				sep = " , ";
			}

		} else {
			cod = "DW 0";
		}
		gen("VT" + clase.name, cod);

	}

	/**
	 * Genera el código para la expresión new
	 */
	private void generateNew(String id) {
		gen("\n.CODE");
		gen("; new");
		gen("RMEM 1");
		// gen("PUSH " + tc.get(id).cirSize);
		gen("PUSH " + tc.get(id).etiqueta);
		gen("PUSH malloc");
		gen("CALL");
		gen("DUP");
		gen("PUSH VT" + id);
		gen("STOREREF 0");
		// gen("# INICIO new");
		gen("DUP");
	}

	private void generateNew2(String id) {
		gen("PUSH " + tc.get(id).constructor.etiqueta);
		gen("CALL");
	}

	/**
	 * Inicialización
	 */
	private void generateInit() {
		gen("\n; clase Object");
		gen(".DATA");
		
		gen("VTObject", "DW 0");	
		gen("$$division_por_cero_msg","DW \"ERROR!!! NO SE PUEDE DIVIDIR POR 0!!\",0");
		
		gen(".CODE");
		
		// gen("RMEM 3");//VER
		gen("PUSH heap_init");
		gen("CALL");

		gen("; creación Main");
		gen("RMEM 1");
		gen("PUSH 11Main11");
		gen("PUSH malloc");
		gen("CALL");
		gen("DUP");
		gen("PUSH VTMain");
		gen("STOREREF 0");
		gen("DUP");

		gen("PUSH Ctor$Main");
		gen("CALL");
		gen("HALT");

		gen("\n ; heap_init + malloc");
		gen("heap_init", "RET 0");
		gen("malloc", "LOADFP");
		gen("LOADSP");
		gen("STOREFP");
		gen("LOADHL");
		gen("DUP");
		gen("PUSH 1");
		gen("ADD");
		gen("STORE 4");
		gen("LOAD 3");
		gen("ADD");
		gen("STOREHL");
		gen("STOREFP");
		gen("RET 1");

		gen(TablaDeClases.CONSTRUCTOR_VACIO.etiqueta, "NOP");
		gen("RET 1");
		
		gen("\n; división por cero");
		gen("$$divison_por_cero","NOP");
		gen("PUSH $$division_por_cero_msg");
		gen("SPRINT");
		gen("HALT");
		
		out = out + nkSystem.out;

	}

	/**
	 * Genera el codigo de la asignacion
	 * 
	 * @param v
	 */
	private void generateAsignacion(Variable v) {
		// lo que está en el tope del stack se lo asigna a v
		// pp("generateAsignacion");
		// pp(v.name+" "+v.esAtributo);
		gen("DUP"); //duplico el resultado
		if (v.esAtributo) {
			gen("LOAD 3"); // el this está siempre en 3
			gen("SWAP");
			// gen("STOREREF "+v.offset);
			gen("STOREREF " + v.etiqueta);
		} else { // es un parámetro
			gen("STORE " + v.offset);
		}
	}

	/**
	 * Apila el valor de la variable v
	 * 
	 * @param v
	 */
	private void generateVariable(Variable v) {
		// pp("generateVariable "+v.name+" "+v.esAtributo);
		if (v.esAtributo) {
			gen("LOAD 3"); // el this está siempre en 3
			// gen("LOADREF "+v.offset);
			gen("LOADREF " + v.etiqueta);
		} else {
			gen("LOAD " + v.offset);
		}
	}

	private void generateConstructorIni() {
		gen("\n; constructor " + clase.name);
		gen(".CODE");
		gen(clase.constructor.etiqueta, "NOP");
		gen("LOADFP");
		gen("LOADSP");
		gen("STOREFP");
	}

	private void generateConstructorFin() {
		int cantParam = clase.constructor.parametros.size();
		// gen("STORE "+(cantParam+4)); //apilo el retorno
		gen("STOREFP");
		gen("RET " + (cantParam + 1));
	}

	private void generateMethodIni() {
		gen("\n; generateMethodIni " + metodo.name);
		gen(".CODE");
		gen(metodo.etiqueta, "NOP");
		gen("LOADFP");
		gen("LOADSP");
		gen("STOREFP");
	}

	private void generateMethodFin() {
		gen("\n; generateMethodFin " + metodo.name);
		int cantParam = metodo.parametros.size();
		if (!metodo.type.equals("void"))
			gen("STORE " + (cantParam + 4)); // apilo el retorno
		gen("STOREFP");
		gen("RET " + (cantParam + 1));
	}

	private void generateSuperCallIni(String id) {
		debug("generateSuperCallIni " + id);
		gen("\n; superCall " + id);
		gen(".CODE");

		Metodo m = clase.parent.allMetodos.get(id);
		gen("LOAD 3"); // this
		if (!m.type.equals("void")) {
			gen("RMEM 1"); // reservo lugar para el valor de retorno
			gen("SWAP");
		}

	}

	private void generateSuperCallFin(String id) {
		Metodo m = clase.parent.allMetodos.get(id);
		// gen("DUP"); //duplico el this
		// gen("LOADREF 0"); //cargo la VT
		// gen("LOADREF "+m.offset);
		gen("PUSH " + m.etiqueta);
		gen("CALL");
	}

	/**
	 * El receptor ya tiene que estar apilado
	 * 
	 * @param id
	 */
	private void generateMethodCallIni(String clase, String id) {
		debug("generateMethodCallIni " + clase + " " + id);
		gen("\n; methodCall " + id);
		gen(".CODE");

		Metodo m = tc.get(clase).allMetodos.get(id);
		if (!m.type.equals("void")) {
			gen("RMEM 1"); // reservo lugar para el valor de retorno
			gen("SWAP");
		}
		// gen("LOAD 3"); //this
	}

	private void generateMethodCallFin(String clase, String id) {
		Metodo m = tc.get(clase).allMetodos.get(id);
		gen("DUP"); // duplico el this
		gen("LOADREF 0"); // cargo la VT
		gen("LOADREF " + m.offset);
		gen("CALL");
	}

	/**
	 * calcula los offsets y cirSize
	 */
	private void calculateOffsetsAndCirSize() {
		for (Clase c : tc.clases.values())
			calculateOffsetsAndCirSize(c);

		for (Clase c : tc.clases.values()) {
			out = out.replaceAll(c.etiqueta, "" + c.cirSize);
			for (Variable v : c.atributos.values())
				out = out.replaceAll(v.etiqueta, "" + v.offset);
		}

	}

	private void calculateOffsetsAndCirSize(Clase c) {
		if (c.cirSize != -1)
			return;
		if (c.parent.cirSize == -1)
			calculateOffsetsAndCirSize(c.parent);
		c.cirSize = c.parent.cirSize + c.atributos.size();
		int pos = c.parent.cirSize;
		for (Variable v : c.atributos.values())
			v.offset = pos++;
	}

	private void generateDivisionByZeroCheck() {
		gen(";chequeo división por 0");
		gen("DUP");
		gen("BT $divisionLabel$");
		gen("PUSH $$divison_por_cero");
		gen("CALL");
		gen("$divisionLabel$","NOP");
	}
	

	
	  /*public static void main(String[] args) {  
		  
		  int a;
		  System.out.println(a = 5);
		  
		  
	  }*/
	 

}